package com.stone.customer.console.api.controller.v1;

import com.alibaba.fastjson.JSON;
import com.stone.starter.redis.client.RedisCacheClient;
import io.choerodon.core.iam.ResourceLevel;
import io.choerodon.swagger.annotation.Permission;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.hzero.core.util.Results;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.ResponseEntity;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.web.bind.annotation.*;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * lua脚本测试类
 *
 * @author Mr_wenpan@163.com 2021/07/15 14:53
 */
@Slf4j
@RestController("LuaScriptTestController.v1")
@RequestMapping("/v1/{organizationId}/lua-test")
public class LuaScriptTestController {

    /**
     * redis服务客户端
     */
    @Autowired
    private RedisCacheClient redisCacheClient;

    private static final String BATCH_ADD_TO_QUEUE = "stone:customer-console:batch-add-queue";
    private static final DefaultRedisScript<Boolean> BATCH_ADD_SCRIPT;
    final String BATCH_ADD_LUA_SCRIPT = "local success = true\n" +
            "local queue = KEYS[1]\n" +
            "local values = ARGV\n" +
            "for k,v in ipairs(values) do\n" +
            "    redis.call('lpush',queue,v)\n" +
            "end\n" +
            "return success";

    static {
        BATCH_ADD_SCRIPT = new DefaultRedisScript<>();
        BATCH_ADD_SCRIPT.setScriptSource(new ResourceScriptSource(new ClassPathResource("script.lua/customer-id-batch-push.lua")));
        BATCH_ADD_SCRIPT.setResultType(Boolean.class);
    }

    @PostMapping("/list-batch-add")
    @ApiOperation(value = "lua脚本批量写入队列测试")
    @Permission(level = ResourceLevel.ORGANIZATION)
    public ResponseEntity<?> batchAddToRedisList(@PathVariable String organizationId) {
        List<String> args = new ArrayList<String>() {{
            add("one");
            add("two");
            add("three");
            add("four");
        }};
        // 注意如果Redis使用的是集群模式，那么spring提供的DefaultRedisScript + RedisTemplate是无法使用的，会报错
        // 参考：https://cloud.tencent.com/developer/article/1613063
        return Results.success(redisCacheClient.execute(BATCH_ADD_SCRIPT,
                Collections.singletonList(BATCH_ADD_TO_QUEUE), args.toArray()));
    }

    @PostMapping("/list-batch-add-adapter")
    @ApiOperation(value = "lua脚本批量写入队列测试(适配单点和集群)")
    @Permission(level = ResourceLevel.ORGANIZATION)
    public ResponseEntity<?> batchAddToRedisListAdapter(@PathVariable String organizationId) {
        List<String> args = new ArrayList<String>() {{
            add("one");
            add("two");
            add("three");
            add("four");
        }};
        //spring自带的执行脚本方法中，集群模式直接抛出不支持执行脚本异常，此处拿到原redis的connection执行脚本
        String result = redisCacheClient.execute((RedisCallback<String>) connection -> {
            // 获取到原生Redis连接
            Object nativeConnection = connection.getNativeConnection();

            // 集群
            // 集群模式和单点模式虽然执行脚本的方法一样，但是没有共同的接口，所以只能分开执行
            if (nativeConnection instanceof JedisCluster) {
                return (String) ((JedisCluster) nativeConnection).eval(BATCH_ADD_LUA_SCRIPT,
                        Collections.singletonList(BATCH_ADD_TO_QUEUE), args);
            }

            // 单点
            else if (nativeConnection instanceof Jedis) {
                return (String) ((Jedis) nativeConnection).eval(BATCH_ADD_LUA_SCRIPT,
                        Collections.singletonList(BATCH_ADD_TO_QUEUE), args);
            }

            return null;
        });
        return Results.success(result);
    }

    @GetMapping("/blpop-test")
    @ApiOperation(value = "阻塞获取Redis队列数据测试")
    public ResponseEntity<?> blpopTest() {
        // list集合 第一个元素为key值，第二个元素为弹出的元素值;当超时返回[null]
        List<Object> objects = redisCacheClient.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                // 队列没有元素会阻塞操作，直到队列获取新的元素或超时
                return connection.bLPop(10, "wenpan-list".getBytes());
            }
            // 指定key的序列化方式
        }, new StringRedisSerializer());

        log.info("=========>>>>>>>>>>>>>objects = {}", objects);
        String json = JSON.toJSONString(objects);
        List<List> lists = JSON.parseArray(json, List.class);
        List<String> list = lists.get(0);
        String queueName = list.get(0);
        String value = list.get(1);
        log.info("queueName = {}，value = {}", queueName, value);

        return Results.success("success");
    }
}
